/**
 * DBE common Src and query adapters
 *
 * @author benoit.pointet
 *
 * @version beta
 */

(function()
{

    var xmlAdapter = {

        cache : {},

        _attrQuery : function(expr) {
            var firstChar = expr.charAt(0);
            if (firstChar === '@') {
                return expr.substring(1);
            }
            return false;
        },

        /**
         * performs a query described by an expr
         * with a single opN as context anchor.
         * @return an array of domNs
         */
        query : function(expr,opN) {
            var nsResolver,
            iterator,
            domN,
            domNs;

            var contextDomN = opN.domN ? opN.domN: window.document;
            var resultType = 0;

            domNs = new $dbe.fun.Set();

            nsResolver = function(prefix) {
                var ns = {
                    'xml': null,
                    'xhtml': 'http://www.w3.org/1999/xhtml',
                    'svg': 'http://www.w3.org/2000/svg'
                };
                var nsStr = ns[prefix] || ns['xml'];
                return nsStr;
            }
            // nsResolver = contextDomN.ownerDocument.createNSResolver(contextDomN);

            // if it queries a direct attribute, we return the parent element node, not the attribute itself
            if (this._attrQuery(expr)) {
                domNs.add(opN.domN);
                return domNs;
            }

            iterator = contextDomN.ownerDocument.evaluate(expr, contextDomN, nsResolver, resultType, null);

            while ((domN = iterator.iterateNext())) {
                domNs.add(domN);
            }
            // console.log('adapter/xml.xPathQuery',expr,contextDomN,resultType,iterator.resultType,domNs.size(),domNs.s);
            return domNs;
        },

        /**
         * @return the string value of given opN
         */
        getValue : function(opN) {
            var uid = opN.uid;
            if (this.cache[uid]) {
                return this.cache[uid];
            }
            var domN = opN.domN;
            var attr;
            // if it is an attribute, we access it throught DOM methods upon parent element
            if ((attr = this._attrQuery(opN.dblN.get('query')))) {
                this.cache[uid] = domN.getAttribute(attr);
                return this.cache[uid];
            }

            this.cache[uid] = (this.cache[uid] = domN.textContent) ? this.cache[uid] : domN.nodeValue;
            // console.log('xml.getValue',opN,domN,this.cache[uid]);
            return this.cache[uid];
        },


        /**
        * sets the value of given opN to given value
        * @return the value set
        */
        setValue : function(opN,v) {
            // console.log('xml.setValue',opN.domN,v);
            this.cache[opN.uid] = null;
            var domN = opN.domN;

            // if it is an attribute, we access it throught DOM methods upon parent element
            if (this._attrQuery(opN.dblN.get('query'))) {
                var attr = this._attrQuery(opN.dblN.get('query'));
                return domN.setAttribute(attr,v);
            }

            if (domN.textContent) {
                domN.textContent = v;
                return domN.textContent;
            }
            if (domN.nodeValue) {
                domN.nodeValue = v;
                return domN.nodeValue;
            }
            if (domN.firstChild && domN.firstChild.nodeValue) {
                domN.firstChild.nodeValue = v;
                return domN.firstChild.nodeValue;
            }
        },

        /**
        * loads the source at a given url
        * @return a domN, if no callback provided,
        * else returns nothing and calls callback
        * with result as parameter when done.
        */
        loadSrc  : function(url,callback) {
            var xml;
            var async = callback ? true: false;
            if (window.XMLHttpRequest)
            {
                xml = new window.XMLHttpRequest();
                if (async) {
                    xml.onreadystatechange = function() {
                        // console.log(url,xml.readyState,xml.status);
                        if (xml.readyState == 4 && xml.status == 200) {
                            callback(xml.responseXML);
                        }
                    };
                }
                xml.open("GET", url, async);
                xml.send("");
                if (!async) {
                    return xml.responseXML;
                }
            }
            // IE 5 and IE 6
            else if (ActiveXObject("Microsoft.XMLDOM"))
            {
                xml = new ActiveXObject("Microsoft.XMLDOM");
                xml.async = true;
                xml.load(url);
                xml.onreadystatechange = function() {
                    if (xml.readyState == 4 && xml.status == 200) {
                        callback(xml.responseXML);
                    }
                };
            }
        }
    };

   

    var styleAdapter = {
        /**
         * performs a query described by an expr
         * with a single opN as context anchor.
         * @return an array of domNs
         */
        query : function(expr,opN) {
            var domNs = new $dbe.fun.Set();
            domNs.add(opN.domN);
            return domNs;
        },

        /**
         * @return the value of given opN
         */
        getValue : function(opN) {
            var domN = opN.domN;
            var p = opN.dblN.get('query');
            // console.log('style.getValue',opN,domN,p,domN.style);
            if (domN.style) {
                if (domN.style[p]) {
                    return domN.style[p];
                } else {
                    return domN.style.getPropertyValue(p, null);
                }
            }
        },


        /**
        * sets the value of given opN to given value
        * @return nothing
        */
        setValue : function(opN,v) {
            var domN = opN.domN;
            var cValue = this.getValue(opN);
            var p = opN.dblN.get('query');
            if (domN.style) {
                if (domN.style[p]) {
                    domN.style[p] = v;
                } else {
                    domN.style.setProperty(p, v, null);
                }
            }
        // console.log('style.setValue',opN,domN,p,cValue,v,this.getValue(opN));
        },

        /**
        * loads the source at a given url
        * @return a domN, if no callback provided,
        * else returns nothing and calls callback
        * with result as parameter when done.
        */
        loadSrc  : function(url,callback) {}
    };


    var nvAdapter = {
        /**
         * performs a query described by an expr
         * with a single opN as context anchor.
         * @return an array of domNs
         */
        query : function(expr,opN,opNQ) {
            var added = false;
            var queriedOpNs = opNQ.scope.get(expr);
            // console.log('op.NQ.updateRef results',ref,that.scope,that.dblN.xmlElt,queriedOpNs.size(),queriedOpNs);
            queriedOpNs.each(function(opN) {
                // console.log('op.NQ.updateRef newOpN',that.scope,that.dblN.xmlElt,opN.dblN.xmlElt);
                if ((!opNQ.opNs.has(opN)) && opNQ.parOp && opNQ.parOp.bindOpN) { // TODO 0.3 refactor clarify these dirty tests
                    var bound = opNQ.parOp.bindOpN(opN, opNQ);
                    opNQ.addOpN(opN);
                    added = true;
                }
            });
            opNQ.updateOpNs();
            if (added) {
                opNQ.events.fire('OpNsAdded');
            }
            return new $dbe.fun.Set();
        },

        /**
         * @return the value of given opN
         */
        getValue : function(opN) {
        },


        /**
        * sets the value of given opN to given value
        * @return nothing
        */
        setValue : function(opN,v) {
        },

        /**
        * loads the source at a given url
        * @return a domN, if no callback provided,
        * else returns nothing and calls callback
        * with result as parameter when done.
        */
        loadSrc  : function(url,callback) {}
    };

    
    var computationAdapter = {
        /**
         * performs a query described by an expr
         * with a single opN as context anchor.
         * @return an array of domNs
         */
        query : function(expr,opN,opNQ) {
            var set = new $dbe.fun.Set();
            set.add({value:''});
            return set;
        },

        /**
         * @return the value of given opN
         */
        getValue : function(opN) {
            return opN.domN.value;
        },


        /**
        * sets the value of given opN to given value
        * @return nothing
        */
        setValue : function(opN,v) {
            opN.domN.value = v;
        },

        /**
        * loads the source at a given url
        * @return a domN, if no callback provided,
        * else returns nothing and calls callback
        * with result as parameter when done.
        */
        loadSrc  : function(url,callback) {}
    };




    // ******* add to dbe adapters map *******
    $dbe.addAdapter('',xmlAdapter);
    $dbe.addAdapter('xml',xmlAdapter);
    $dbe.addAdapter('dbl',xmlAdapter);
    $dbe.addAdapter('xpath',xmlAdapter);
    $dbe.addAdapter('style',styleAdapter);
    $dbe.addAdapter('node',nvAdapter);
    $dbe.addAdapter('var',computationAdapter);
})();